package com.zhichat.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.zhichat.dao.*;
import com.zhichat.document.ChatMsgDoc;
import com.zhichat.entity.*;
import com.zhichat.infrastructure.Enums.*;
import com.zhichat.infrastructure.constants.Constants;
import com.zhichat.infrastructure.exception.http.CommonException;
import com.zhichat.infrastructure.redis.Keys;
import com.zhichat.infrastructure.redis.RedisRepository;
import com.zhichat.infrastructure.redis.rediskey.Key;
import com.zhichat.infrastructure.redis.rediskey.ZhiChat;
import com.zhichat.infrastructure.util.ThreadLocalCache;
import com.zhichat.infrastructure.util.Utils;
import com.zhichat.service.ContactInfoService;
import com.zhichat.service.SysConfigService;
import com.zhichat.web.dto.ContactApplyDTO;
import com.zhichat.web.dto.DelContactDTO;
import com.zhichat.web.socket.ChannelContextCommon;
import com.zhichat.web.socket.GlobalMsgHandle;
import com.zhichat.web.vo.ContactItemVO;
import com.zhichat.web.vo.UserInfoVO;
import com.zhichat.web.vo.UserSearchVO;
import com.zhichat.web.vo.ws.MsgVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
@RequiredArgsConstructor
public class ContactInfoServiceImpl extends ServiceImpl<ContactInfoDao, ContactInfoEntity> implements ContactInfoService {

    private final GroupInfoDao groupInfoDao;
    private final UserInfoDao userInfoDao;
    private final ApplyInfoDao applyInfoDao;
    private final SysConfigService sysConfigService;
    private final RedisRepository redisRepository;
    private final GlobalMsgHandle globalMsgHandle;
    private final ChatSessionDao chatSessionDao;
    private final ChatUserSessionDao chatUserSessionDao;
    private final MongoTemplate mongoTemplate;
    private final ChannelContextCommon channelContextCommon;

    @Override
    public UserSearchVO search(String contactId) {
        // 校验是否在user表能找到该用户
        LambdaQueryWrapper<UserInfoEntity> userInfoWrapper = Wrappers.lambdaQuery(UserInfoEntity.class);
        userInfoWrapper.select(UserInfoEntity::getId, UserInfoEntity::getUserId, UserInfoEntity::getNickname
                        , UserInfoEntity::getGender, UserInfoEntity::getAreaName)
                .eq(UserInfoEntity::getUserId, contactId)
                .or()
                .eq(UserInfoEntity::getPhone, contactId);
        UserInfoEntity checkUser = userInfoDao.selectOne(userInfoWrapper);
        if (Objects.isNull(checkUser)) {
            throw new CommonException("用户不存在");
        }
        Long userId = checkUser.getId();
        // 查看 联系人 是否有该关系数据
        LambdaQueryWrapper<ContactInfoEntity> contactWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        contactWrapper.select(ContactInfoEntity::getStatus)
                .eq(ContactInfoEntity::getContactId, userId)
                .eq(ContactInfoEntity::getUserId, ThreadLocalCache.getUserId());
        ContactInfoEntity contactInfoEntity = baseMapper.selectOne(contactWrapper);
        // 定义 VO
        UserSearchVO vo = new UserSearchVO();
        vo.setStatus(ContactStatusEnum.NON_FRIEND.getCode());
        vo.setUserId(userId);
        if (Objects.nonNull(contactInfoEntity)) {
            vo.setStatus(contactInfoEntity.getStatus());
        }
        // 如果用户是自己本人
        if (Objects.equals(ThreadLocalCache.getUserId(), userId)) {
            vo.setStatus(ContactStatusEnum.FRIEND.getCode());
        }
        BeanUtils.copyProperties(checkUser, vo, "status", "userId");
        vo.setUserId2(checkUser.getUserId());
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer apply(ContactApplyDTO dto) {
        Long contactId = dto.getContactId();
        Integer contactType = dto.getContactType();
        String applyMsg = dto.getApplyMsg();
        if (ContactTypeEnum.getEnum(contactType) == null) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        if (StrUtil.isEmpty(applyMsg)) {
            applyMsg = StrUtil.format(Constants.CONTACT_APPLY_MESSAGE_TEMPLATE, ThreadLocalCache.getUserToken().getNickname());
        }
        // 查看联系人是否已将你拉黑
        LambdaQueryWrapper<ContactInfoEntity> queryWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        queryWrapper.select(ContactInfoEntity::getStatus)
                .eq(ContactInfoEntity::getContactId, contactId)
                .eq(ContactInfoEntity::getUserId, ThreadLocalCache.getUserId());
        ContactInfoEntity contactInfoEntity = baseMapper.selectOne(queryWrapper);
        if (Objects.nonNull(contactInfoEntity)) {
            if (Objects.equals(contactInfoEntity.getStatus(), ContactStatusEnum.BLACKLIST_FRIEND.getCode())) {
                throw new CommonException("需先解除拉黑状态");
            }
            if (Objects.equals(contactInfoEntity.getStatus(), ContactStatusEnum.BLACKLISTED_BY_FRIEND.getCode())) {
                throw new CommonException("已被对方拉黑");
            }
            if (Objects.equals(contactInfoEntity.getStatus(), ContactStatusEnum.FRIEND.getCode())) {
                throw new CommonException("已是好友");
            }
        }
        Long applyId = ThreadLocalCache.getUserId();
        Long receiveId = contactId;
        Integer joinType = null;
        String contactNickname = null;
        LocalDateTime curDate = LocalDateTime.now();
        // 判断 是群 还是用户（获取添加类型）
        if (Objects.equals(ApplyTypeEnum.USER.getCode(), contactType)) {
            LambdaQueryWrapper<UserInfoEntity> userWrapper = Wrappers.lambdaQuery(UserInfoEntity.class);
            userWrapper.select(UserInfoEntity::getNickname, UserInfoEntity::getJoinType)
                    .eq(UserInfoEntity::getId, contactId);
            UserInfoEntity userInfoEntity = userInfoDao.selectOne(userWrapper);
            if (Objects.isNull(userInfoEntity)) {
                throw CommonException.PARAMETER_COMMON_EXCEPTION;
            }
            joinType = userInfoEntity.getJoinType();
            contactNickname = userInfoEntity.getNickname();
        } else {
            LambdaQueryWrapper<GroupInfoEntity> groupWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
            groupWrapper.select(GroupInfoEntity::getGroupName, GroupInfoEntity::getGroupOwnerId, GroupInfoEntity::getJoinType)
                    .eq(GroupInfoEntity::getGroupId, contactId)
                    .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode());
            GroupInfoEntity groupInfoEntity = groupInfoDao.selectOne(groupWrapper);
            if (Objects.isNull(groupInfoEntity)) {
                throw CommonException.PARAMETER_COMMON_EXCEPTION;
            }
            receiveId = groupInfoEntity.getGroupOwnerId();
            joinType = groupInfoEntity.getJoinType();
            contactNickname = groupInfoEntity.getGroupName();
        }
        // 校验 申请 时间（申请至少间隔1个小时，可配置，例如：间隔1分钟）
        LambdaQueryWrapper<ApplyInfoEntity> applyWrapper = Wrappers.lambdaQuery(ApplyInfoEntity.class);
        applyWrapper.select(ApplyInfoEntity::getId, ApplyInfoEntity::getStatus, ApplyInfoEntity::getLastApplyTime)
                .eq(ApplyInfoEntity::getApplyId, applyId)
                .eq(ApplyInfoEntity::getContactId, contactId)
                .eq(ApplyInfoEntity::getReceiveId, receiveId);
        ApplyInfoEntity applyInfoEntity = applyInfoDao.selectOne(applyWrapper);
        if (Objects.nonNull(applyInfoEntity)) {
            Long lastApplyTime = applyInfoEntity.getLastApplyTime();
            if (DateUtil.date(curDate).getTime() - Constants.CONTACT_APPLY_INTERVAL < lastApplyTime) {
                String resMsg = Objects.equals(ApplyStatusEnum.BLACKED.getCode(), applyInfoEntity.getStatus()) ? "已被对方拉黑" : "申请已提交，请勿重复申请";
                throw new CommonException(resMsg);
//                if (Objects.equals(ApplyStatusEnum.APPLYING.getCode(), applyInfoEntity.getStatus())) {
//                    return UserJoinTypeEnum.NEED_VERIFY.getCode();
//                } else if (Objects.equals(ApplyStatusEnum.APPROVED.getCode(), applyInfoEntity.getStatus())) {
//                    throw CommonException.PARAMETER_COMMON_EXCEPTION;
//                } else if () {
//                    throw new CommonException("已被对方拉黑");
//                } else {
//
//                }
            }
        }
        // 直接加入联系人  -  不管是群，还是用户，1都代表不用验证
        if (Objects.equals(UserJoinTypeEnum.ALLOW_ANY.getCode(), joinType)) {
            Document paramDoc = new Document();
            paramDoc.put("applyId", applyId);
            paramDoc.put("applyNickname", ThreadLocalCache.getUserToken().getNickname());
            paramDoc.put("receiveId", receiveId);
            paramDoc.put("contactId", contactId);
            paramDoc.put("contactNickname", contactNickname);
            paramDoc.put("contactType", contactType);
            paramDoc.put("applyMsg", applyMsg);
            paramDoc.put("curDate", curDate);
            this.addContactInfo(paramDoc);
            return GroupJoinTypeEnum.ANYONE.getCode();
        }
        // 需要提交申请验证
        // 保存信息 -> 联系人申请表
        ApplyInfoEntity applyInfo = new ApplyInfoEntity();
        applyInfo.setStatus(ApplyStatusEnum.APPLYING.getCode());
        applyInfo.setLastApplyTime(DateUtil.date(curDate).getTime());
        applyInfo.setApplyMessage(applyMsg);
        if (Objects.isNull(applyInfoEntity)) {
            // 直接新增
            applyInfo.setApplyId(applyId);
            applyInfo.setReceiveId(receiveId);
            applyInfo.setContactType(contactType);
            applyInfo.setContactId(contactId);
            if (1 != applyInfoDao.insert(applyInfo)) {
                throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
            }
        } else {
            // 修改信息
            applyInfo.setId(applyInfoEntity.getId());
            if (1 != applyInfoDao.updateById(applyInfo)) {
                throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
            }
        }
        // 发送 ws 申请消息
        MsgVO<String> msgVO = new MsgVO<String>();
        msgVO.setReceiverId(receiveId)
                .setReceiverType(ContactTypeEnum.USER.getCode())
                .setMsgType(MsgTypeEnum.FRIEND_APPLY.getCode())
                .setMsg(applyMsg);
        globalMsgHandle.sendHandler(msgVO);
        return joinType;
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void addContactInfo(Document paramDoc) {
        Long applyId = paramDoc.getLong("applyId");
        String applyNickname = paramDoc.getString("applyNickname");
        Long contactId = paramDoc.getLong("contactId");
        String contactNickname = paramDoc.getString("contactNickname");
        Integer contactType = paramDoc.getInteger("contactType");
        String applyMsg = Objects.equals(ContactTypeEnum.USER.getCode(), contactType) ? paramDoc.getString("applyMsg")
                : StrUtil.format(MsgTypeEnum.ADD_GROUP.getInitMsg(), applyNickname);
        Key key = null;
        boolean flag = Objects.equals(ContactTypeEnum.GROUP.getCode(), contactType);
        String sessionId = flag ? Utils.lsMd5(contactId) : Utils.lsMd5(applyId, contactId);
        paramDoc.put("sessionId", sessionId);
        LocalDateTime nowDateTime = paramDoc.get("curDate", LocalDateTime.class);
        if (flag) {
            // 如果是群，判断群聊人数有没有达到设置的阀值
            SysConfigEntity sysConfigInfo = sysConfigService.getSysConfigInfo();
            LambdaQueryWrapper<ContactInfoEntity> queryWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
            queryWrapper.eq(ContactInfoEntity::getContactId, contactId)
                    .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
            int count = Math.toIntExact(baseMapper.selectCount(queryWrapper));
            if (sysConfigInfo.getMaxGroupCount() <= count) {
                throw new CommonException("群聊人数已达到上限");
            }
            key = new ZhiChat(Keys.CONTACT_GROUP);
        } else {
            // 如果是用户，联系人要相互是好友
            contactSaveOrUpdate(contactId, applyId, contactType, nowDateTime);
            key = new ZhiChat(Keys.CONTACT_USER);
        }
        // 添加联系人（即时是群组，也要这么一步处理）
        contactSaveOrUpdate(applyId, contactId, contactType, nowDateTime);
        // 添加会话信息(不管是用户、群) -> mysql
        addSessionInfo(paramDoc);
        // 添加聊天Msg -> mongodb
        ChatMsgDoc chatMsgDoc = new ChatMsgDoc();
        try {
            chatMsgDoc.setId(Utils.uniqueId())
                    .setSessionId(sessionId)
                    .setSenderId(applyId)
                    .setSenderNickname(applyNickname)
                    .setSendTime(DateUtil.date(nowDateTime).getTime())
                    .setReceiverId(contactId)
                    .setReceiverType(contactType)
                    .setMsg(applyMsg)
                    .setMsgType(!flag ? MsgTypeEnum.ADD_FRIEND.getCode() : MsgTypeEnum.ADD_GROUP.getCode())
                    .setStatus(ChatMsgStatusEnum.SUCCESS.getCode());
            mongoTemplate.insert(chatMsgDoc);
        } catch (Exception e) {
            throw CommonException.MONGO_MODIFY_COMMON_EXCEPTION;
        }
        // 添加联系人 -> redis（TODO 申请人不在线，不需要存入redis）
        redisRepository.add_2(key, Sets.newHashSet(contactId), applyId);
        // 发送 ws 消息
        MsgVO<String> msgVO = new MsgVO<>();
        BeanUtils.copyProperties(chatMsgDoc, msgVO);
        msgVO.setMsgId(chatMsgDoc.getId())
                .setReceiverNickname(contactNickname);
        if (flag) {
            // 如果是群 添加申请人channel -> 群channel
            channelContextCommon.putAllUserChannelToGroupChannel(contactId, Lists.newArrayList(channelContextCommon.getUserChannel(applyId)));
            LambdaQueryWrapper<ContactInfoEntity> contactQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
            contactQuery.eq(ContactInfoEntity::getContactId, contactId)
                    .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
            Integer count = Math.toIntExact(baseMapper.selectCount(contactQuery));
            msgVO.setMemberCount(count);
        } else {
            // 如果是用户，相互都要发送ws消息
            redisRepository.add_2(key, Sets.newHashSet(applyId), contactId);
            MsgVO<String> msgVO1 = new MsgVO<>();
            BeanUtils.copyProperties(msgVO, msgVO1);
            msgVO1.setReceiverId(applyId)
                    .setReceiverNickname(applyNickname);
            globalMsgHandle.sendHandler(msgVO1);
        }
        globalMsgHandle.sendHandler(msgVO);
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void addSessionInfo(Document paramDoc) {
        List<ChatUserSessionEntity> resList = Lists.newArrayList();
        Long applyId = paramDoc.getLong("applyId");
        String applyNickname = paramDoc.getString("applyNickname");
        Long contactId = paramDoc.getLong("contactId");
        String contactNickname = paramDoc.getString("contactNickname");
        String applyMessage = paramDoc.getString("applyMsg");
        String sessionId = paramDoc.getString("sessionId");
        LocalDateTime nowDateTime = paramDoc.get("curDate", LocalDateTime.class);
        Integer contactType = paramDoc.getInteger("contactType");
        // 添加会话、用户会话 -> mysql
        ChatSessionEntity checkChatSessionEntity = chatSessionDao.selectById(sessionId);
        ChatSessionEntity chatSessionEntity = new ChatSessionEntity()
                .setId(sessionId)
                .setLastMsg(applyMessage)
                .setLastReceiveTime(DateUtil.date(nowDateTime).getTime());
        if (Objects.isNull(checkChatSessionEntity)) {
            chatSessionDao.insert(chatSessionEntity);
            ChatUserSessionEntity applyChatUserSessionEntity = new ChatUserSessionEntity()
                    .setUserId(applyId)
                    .setReceiverId(contactId)
                    .setSessionId(sessionId)
                    .setReceiverType(contactType)
                    .setReceiverNickname(contactNickname);
            resList.add(applyChatUserSessionEntity);
            if (Objects.equals(ContactTypeEnum.USER.getCode(), contactType)) {
                ChatUserSessionEntity contactChatUserSessionEntity = new ChatUserSessionEntity()
                        .setUserId(contactId)
                        .setReceiverId(applyId)
                        .setSessionId(sessionId)
                        .setReceiverType(contactType)
                        .setReceiverNickname(applyNickname);
                resList.add(contactChatUserSessionEntity);
            }
        } else {
            chatSessionDao.updateById(chatSessionEntity);
        }
        if (!CollectionUtils.isEmpty(resList)) {
            chatUserSessionDao.bulk(resList);
        }
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void contactSaveOrUpdate(Long applyId, Long contactId, Integer contactType, LocalDateTime nowDateTime) {
        ContactInfoEntity applyUserContactInfoEntity = new ContactInfoEntity();
        applyUserContactInfoEntity.setUserId(applyId);
        applyUserContactInfoEntity.setContactId(contactId);
        applyUserContactInfoEntity.setContactType(contactType);
        applyUserContactInfoEntity.setLastUpdateTime(LocalDateTimeUtil.toEpochMilli(nowDateTime));
        applyUserContactInfoEntity.setStatus(ContactStatusEnum.FRIEND.getCode());
//        if (baseMapper.insertOrUpdate(applyUserContactInfoEntity) != 1) {
//            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
//        }
        LambdaQueryWrapper<ContactInfoEntity> checkQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        checkQuery.eq(ContactInfoEntity::getUserId, applyId)
                .eq(ContactInfoEntity::getContactId, contactId);
        Integer checkCount = Math.toIntExact(baseMapper.selectCount(checkQuery));
        if (checkCount < 1) {
            if (!save(applyUserContactInfoEntity)) {
                throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
            }
        } else {
            LambdaUpdateWrapper<ContactInfoEntity> contactInfoEntityLambdaUpdateWrapper = Wrappers.lambdaUpdate(ContactInfoEntity.class);
            contactInfoEntityLambdaUpdateWrapper.eq(ContactInfoEntity::getUserId, applyId)
                    .eq(ContactInfoEntity::getContactId, contactId);
            boolean update = update(applyUserContactInfoEntity, contactInfoEntityLambdaUpdateWrapper);
            if (!update) {
                throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
            }
        }
    }

    @Override
    public List<ContactItemVO> contactList(String type) {
        if (!StringUtils.hasText(type) || Objects.isNull(ContactTypeEnum.valueOf(type))) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        List<ContactItemVO> contactList = baseMapper.getContactList(ThreadLocalCache.getUserId(), ContactTypeEnum.valueOf(type).getCode());
        return contactList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delContact(DelContactDTO dto) {
        Long contactId = dto.getContactId();
        Integer contactType = dto.getContactType();
        Integer status = dto.getStatus();
        if (ContactTypeEnum.getEnum(contactType) == null || ObjectUtil.isNull(ContactStatusEnum.getEnumByCode(status))
                || !Objects.equals(ContactStatusEnum.DELETED_FRIEND.getCode(), status)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        Long userId = ThreadLocalCache.getUserId();
        LocalDateTime curLocalDateTime = LocalDateTime.now();
        boolean isUserFlag = Objects.equals(ContactTypeEnum.USER.getCode(), contactType);
        // 校验数据库是否有该联系人关系
        LambdaQueryWrapper<ContactInfoEntity> queryWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        queryWrapper.select(ContactInfoEntity::getStatus)
                .eq(ContactInfoEntity::getUserId, userId)
                .eq(ContactInfoEntity::getContactId, contactId)
                .eq(ContactInfoEntity::getContactType, contactType)
                .last(" for update");
        ContactInfoEntity contactInfoEntity = baseMapper.selectOne(queryWrapper);
        if (Objects.isNull(contactInfoEntity)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        Integer dbStatus = contactInfoEntity.getStatus();
        if (Objects.equals(ContactStatusEnum.BLACKLIST_FRIEND.getCode(), dbStatus)) {
            throw new CommonException("需要先将联系人从黑名单中删除");
        }
        if (Objects.equals(status, dbStatus)) {
            throw new CommonException("已删除");
        }
        /*
         * 用户 - 传入的是 删除 数据库的状态可以是好友、被删除、被拉黑
         * 群组 - 传入的是 删除 数据库的状态必须是好友（退群）
         */
        if ((isUserFlag && !ArrayUtil.contains(new Integer[]{ContactStatusEnum.FRIEND.getCode(), ContactStatusEnum.BLACKLISTED_BY_FRIEND.getCode()
                , ContactStatusEnum.DELETED_BY_FRIEND.getCode()}, dbStatus))
                || (!isUserFlag && !Objects.equals(ContactStatusEnum.FRIEND.getCode(), dbStatus))) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        // 修改数据：
        //     用户：同时修改俩条数据（我和联系人，联系人和我）
        //     群组：只修改一条数据（我）
        LambdaUpdateWrapper<ContactInfoEntity> contactUpdateWrapper = Wrappers.lambdaUpdate(ContactInfoEntity.class);
        contactUpdateWrapper.set(ContactInfoEntity::getStatus, status)
                .eq(ContactInfoEntity::getUserId, userId)
                .eq(ContactInfoEntity::getContactId, contactId)
                .eq(ContactInfoEntity::getStatus, dbStatus);
        if (0 == baseMapper.update(null, contactUpdateWrapper)) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        if (isUserFlag) {
            LambdaQueryWrapper<ContactInfoEntity> checkContactQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
            checkContactQuery.select(ContactInfoEntity::getStatus)
                    .eq(ContactInfoEntity::getUserId, contactId)
                    .eq(ContactInfoEntity::getContactId, userId);
            ContactInfoEntity checkContactEntity = this.getOne(checkContactQuery);
            if (Objects.isNull(checkContactEntity) || (Objects.nonNull(checkContactEntity) && ArrayUtil.contains(new Integer[]{ContactStatusEnum.FRIEND.getCode()
                    , ContactStatusEnum.BLACKLISTED_BY_FRIEND.getCode()}, dbStatus))) {
                ContactInfoEntity saveOrUpdate = new ContactInfoEntity()
                        .setUserId(contactId)
                        .setContactId(userId)
                        .setContactType(contactType)
                        .setLastUpdateTime(LocalDateTimeUtil.toEpochMilli(curLocalDateTime))
                        .setStatus(ContactStatusEnum.DELETED_BY_FRIEND.getCode());
                if (0 == baseMapper.insertOrUpdate(saveOrUpdate)) {
                    throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
                }
            }
        }
        // 处理缓存
        Key key = new ZhiChat(isUserFlag ? Keys.CONTACT_USER : Keys.CONTACT_GROUP);
        redisRepository.sRemove(key, Sets.newHashSet(contactId), userId);
        if (isUserFlag) {
            redisRepository.sRemove(key, Sets.newHashSet(userId), contactId);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean blackoutContact(DelContactDTO dto) {
        Long contactId = dto.getContactId();
        Integer contactType = dto.getContactType();
        Integer status = dto.getStatus();
        if (ContactTypeEnum.getEnum(contactType) == null || !Objects.equals(ContactStatusEnum.BLACKLIST_FRIEND.getCode(), status)
                || Objects.equals(ContactTypeEnum.GROUP.getCode(), contactType)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        Long userId = ThreadLocalCache.getUserId();
        LocalDateTime curLocalDateTime = LocalDateTime.now();
        LambdaQueryWrapper<ContactInfoEntity> queryWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        queryWrapper.select(ContactInfoEntity::getStatus)
                .eq(ContactInfoEntity::getUserId, userId)
                .eq(ContactInfoEntity::getContactId, contactId)
                .eq(ContactInfoEntity::getContactType, contactType)
                .last(" lock in share mode");
        ContactInfoEntity contactInfoEntity = baseMapper.selectOne(queryWrapper);
        if (Objects.isNull(contactInfoEntity)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        Integer dbStatus = contactInfoEntity.getStatus();
        if (Objects.equals(dbStatus, status)) {
            throw new CommonException("已拉黑");
        }
        // 修改数据：
        //     用户：同时修改俩条数据（我和联系人，联系人和我）
        ContactInfoEntity insertOrUpdateEntity = new ContactInfoEntity()
                .setContactId(contactId)
                .setUserId(userId)
                .setContactType(contactType)
                .setLastUpdateTime(LocalDateTimeUtil.toEpochMilli(curLocalDateTime))
                .setStatus(status);
        if (0 == baseMapper.insertOrUpdate(insertOrUpdateEntity)) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        LambdaQueryWrapper<ContactInfoEntity> checkContactQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        checkContactQuery.select(ContactInfoEntity::getStatus)
                .eq(ContactInfoEntity::getUserId, contactId)
                .eq(ContactInfoEntity::getContactId, userId);
        ContactInfoEntity checkContactEntity = this.getOne(checkContactQuery);
        if (!(Objects.nonNull(checkContactEntity) && (Objects.equals(ContactStatusEnum.BLACKLISTED_BY_FRIEND.getCode(), checkContactEntity.getStatus())
                || Objects.equals(ContactStatusEnum.BLACKLIST_FRIEND.getCode(), checkContactEntity.getStatus())))) {
            ContactInfoEntity saveOrUpdate = new ContactInfoEntity()
                    .setUserId(contactId)
                    .setContactId(userId)
                    .setContactType(contactType)
                    .setLastUpdateTime(LocalDateTimeUtil.toEpochMilli(curLocalDateTime))
                    .setStatus(ContactStatusEnum.BLACKLISTED_BY_FRIEND.getCode());
            if (0 == baseMapper.insertOrUpdate(saveOrUpdate)) {
                throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
            }
        }
        // 处理缓存
        Key key = new ZhiChat(Keys.CONTACT_USER);
        redisRepository.sRemove(key, Sets.newHashSet(contactId), userId);
        redisRepository.sRemove(key, Sets.newHashSet(userId), contactId);
        return true;
    }

    @Override
    public UserInfoVO userDetail(Long userId) {
        if (0 == userId) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        // 是否在我的好友列表内
        Key key = new ZhiChat(Keys.CONTACT_USER);
        Boolean isMember = redisRepository.isMember(key, userId, ThreadLocalCache.getUserId());
        if (!isMember) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        LambdaQueryWrapper<ContactInfoEntity> contactWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        contactWrapper.select(ContactInfoEntity::getStatus)
                .eq(ContactInfoEntity::getUserId, ThreadLocalCache.getUserId())
                .eq(ContactInfoEntity::getContactId, userId);
        ContactInfoEntity contactInfoEntity = baseMapper.selectOne(contactWrapper);
        // 查询用户信息
        LambdaQueryWrapper<UserInfoEntity> queryWrapper = Wrappers.lambdaQuery(UserInfoEntity.class);
        queryWrapper.eq(UserInfoEntity::getId, userId);
        UserInfoEntity userInfoEntity = userInfoDao.selectOne(queryWrapper);
        UserInfoVO resVo = new UserInfoVO();
        BeanUtils.copyProperties(userInfoEntity, resVo, "userId");
        resVo.setUserId(userId);
        resVo.setUserId2(userInfoEntity.getUserId());
        resVo.setContactStatus(contactInfoEntity.getStatus());
        return resVo;
    }
}